/**
 * 手写promise主要的一些方法，理解其常用方法的原理，做到较为透彻的理解即可
 * 
 */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECT = 'reject';

class MyPromise {
    PromisState = PENDING;
    PromiseResult = undefined;

    fulfilledCbs = [];
    rejectCbs = [];

    constructor (exectutor) {
        try {
            exectutor(this.resolve.bind(this), this.reject.bind(this));
        } catch (err) {
            this.reject(err);
        }
    }

    resolve (val) {
        if (this.PromisState !== PENDING) return;
        this.PromisState = FULFILLED;
        this.PromiseResult = val;
        while (this.fulfilledCbs.length) {
            this.fulfilledCbs.shift()(this.PromiseResult);
        }
    }

    reject (reason) {
        if (this.PromisState !== PENDING) return;
        this.PromisState = FULFILLED;
        this.PromiseResult = reason;
        while (this.rejectCbs.length) {
            this.rejectCbs.shift()(this.PromiseResult);
        }
    }

    /**
     * then 方法
     * 1，接受两个回调函数0
     * 2，如果状态变为成功状态，则执行第一个回调
     * 3，如果状态变为失败状态，则执行第二个回调
     * 4，如果状态变为待定状态，则保留当前的两个回调
     * 
     * 
     * then 会返回一个新的 Promise 对象，该对象的状态和结果由回调函数的返回值决定
     * 如果返回值是 promise 对象，
     *     返回值为成功，新promise 就是成功
     *     返回值为失败，新promise 就是失败
     * 如果返回值非promise对象
     *     新promise就是成功，它的值就是返回值
     */
    then (onFulfilled, onRejected) {
        // 参数判断
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
        const thenPromise = new MyPromise((resolve, reject) => {
            const resolvePromise = cb => {
                queueMicrotask(() => { // 异步方法，让 thenPromise 先进行赋值操作
                    try {
                        let x = cb(this.PromiseResult);
                        if (x === thenPromise) {
                            throw new Error('then lian 错误');
                        }
                        if (x instanceof MyPromise) {
                            x.then(resolve, reject);
                        } else {
                            resolve(x);
                        }
                    } catch (err) {
                        console.error(err);
                        reject(err);
                    }
                });
            };
            if (this.PromisState === FULFILLED) {
                resolvePromise(onFulfilled);
            } else if (this.PromisState === REJECT) {
                resolvePromise(onRejected);
            } else {
                this.fulfilledCbs.push(resolvePromise.bind(this, onFulfilled));
                this.rejectCbs.push(resolvePromise.bind(this, onFulfilled));
            }
        });
        return thenPromise;
    }

    /**
     * all 是一个静态方法，需要一个数组作为参数，返回一个 Promise
     *   参数数组中，如果所有 promise 对象都为成功，则返回成功状态的 promise对象
     *   参数数组中，如果有一个 promise 对象为失败，则返回失败状态的 promise对象
     */

    static all (arr) {
        let result = [];
        let count = 0;
        return new MyPromise ((resolve, reject) => {
            const addData = (index, val) => {
                result[index] = val;
                count++;
                if (count === arr.length) resolve(result);
            }
            arr.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(val =>{
                        addData(index, val);
                    }, reject);
                } else addData(index, item);
            });
        });
    }

    static race (arr) {
        return new MyPromise ((resolve, reject) => {
            arr.forEach(item => {
                if (item instanceof MyPromise) {
                    item.then(resolve, reject);
                } else {
                    queueMicrotask(() => {
                        resolve(item);
                    });
                }
            });
        });
    }

    static resolve (val) {
        if (val instanceof MyPromise) return val;
        return new MyPromise(resolve => {
            resolve(val);
        });
    }

    static reject (reason) {
        return new MyPromise((resolce, reject) => reject(reason));
    }

    /**
     * finally 使用时，形如 A.finally(p1).then();
     *   注意：要等 p1 执行完毕后，才会执行 then
     * 
     *   如果 p1 为成功 promise, then 接收 A对象 的结果
     *   如果 p1 为失败 promise, then 接收 p1对象 的结果
     * @param {*} callback 
     * @returns 
     */
    finally (callback) {
        let x = typeof callback === 'function' ? callback() : callback; // 做代码容错，提高健壮性
        return MyPromise.resolve(x).then(() => this);
        // 为什么不写成下面这种形式
        // 1, 错误的回调中为什么要用 throw, 因为 throw 可以保存当前的错误状态
        // 2, 为什么错误的回调要删除不写，因为在 MyPromise 的 then 方法中，对传入的成功回调和错误回调做了容错处理，当前的错误回调形式与 then 中默认的错误回调相同，故在此处省略不写
        // return MyPromise.resolve(x).then(() => this, reason => { throw reason });
    }

    /**
     * catch 方法只会在 promise 错误时触发
     */
    catch (onRejected) {
        return this.then(null, onRejected);
    }
}
